#ifndef _INPUT_H_
#define _INPUT_H_

#include "common/preclude.h"
#include "common/common.h"
#include "common/type.h"

//强制启用引擎代理输入事件
#define _UENGINE_INPUTSYSTEM_AGENT_EVENT_
#include "input_agent_assert.h"
#include "keycode.h"

using InputEventFunc = std::function<void(void)>;

/*
 * 输入系统
 * 用于管理所有的输入消息
 * 当有输入产生时，将消息传递到此对象
 * 此对象存在一个全局对象Inputer，当需要使用输入系统的操作时，通过此对象获取
 *
 * EventType：
 * 产生输入消息的类型，可以是未知类型，按键按下，按键松开，鼠标按钮按下，鼠标按钮松开，亦或是其他的事件(暂未实现)
 *
 * BindEvent函数：
 * 该函数通过一个按键，一个输入消息类型，一个绑定输入事件来绑定一个按键到一个事件
 * 当该按键按下，并且是其所关心的输入类型，会自动调用绑定的事件
 * 其底层是窗口类处理消息函数里，当产生按键按下时，检查其所在按键是否为关心的输入类型
 * 若其是，则检查是否已经绑定了事件，有则运行之
 * 若要取消绑定一个事件，调用UnbindEvent函数
 *
 * 输入系统绑定的事件区别于普通的绑定事件
 * 其运行于窗口事件处理阶段，而普通的自定义事件运行于事件运行线程中
 */

class UInput
{
public:
    enum class InputType
    {
        Unknown,

        KeyDown,
        KeyRelease,

        MouseButtonDown,
        MouseButtonRelease,

        // other input device
    };

    friend class UGameWindow;
    friend class UEditorWindow;

    UInput();
    ~UInput();

    // bind a function be input event,when key event happend,call this function
    void BindEvent(EKeyCode key, UInput::InputType type, InputEventFunc ev);
    // unbind a function for this key and it input type,if not bind any event,ignore
    void UnbindEvent(EKeyCode key, UInput::InputType type);
    // return current key has beed binded input event
    uBoolean KeyHasBindEvent(EKeyCode key);

    // using SDL_GetKeyboardState() && key_state[keyCode] get state
    // this api keepping check state of key,if you want check key once only
    // use bind event for key down action
    uBoolean GetKey(EKeyCode key);
    // using input system
    // this api keepping check state of mouse key
    uBoolean GetMouse(EMouseButton mouse);
    // get mouse positoin by input system,not low layer api
    uPoint<int32> GetMousePosition();
    // get mouse position by sdl api,will get hardware infomation of mouse 0
    uPoint<int32> GetMousePositionFromAgent();

private:
    // Keyboard and Mouse
    void __update_mouse();
    //事件绑定映射
    using __key_events_pack = std::pair<UInput::InputType, InputEventFunc>;
    std::unordered_map<EKeyCode, UInput::__key_events_pack> m_key_events;
    // SDL Keyboard states,使用硬件中断来获取按键状态，当启用了引擎代理事件此变量无值
    uint8 *m_key_states;
    //此变量仅在启用引擎代理事件之后有效
    std::unordered_map<EKeyCode,uBoolean> m_keys_map;
    //
    uPoint<int32> m_mouse_position;
    std::unordered_map<EMouseButton, uBoolean> m_mouse_button;
};

/* 
* GInputer是一个全局对象
* 用于做一些不依赖于窗口的输入消息处理和事件传递
*/
extern UInput *GInputer;

#endif